package com.konka.spring.framework.webmvc.servlet;

import com.konka.spring.framework.annotation.*;
import com.konka.spring.framework.context.ApplicationContext;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author konka
 * @date 2023/2/7
 * @description:
 */
public class DispatcherServlet extends HttpServlet {
    /**
     * HandlerMapping集合
     */
    private List<HandlerMapping> handlerMappings = new CopyOnWriteArrayList<>();
    /**
     * HandlerMapping映射HandlerAdapter
     */
    private Map<HandlerMapping,HandlerAdapter> handlerAdapters = new ConcurrentHashMap<>();
    /**
     * 视图集合
     */
    private List<ViewResolver> viewResolvers = new CopyOnWriteArrayList<>();
    /**
     * 容器上下文
     */
    private ApplicationContext applicationContext = null;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatch(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500 Detail:" + Arrays.toString(e.getStackTrace()));
        }
    }

    /**
     * 执行路由到的方法
     * @param req
     * @param resp
     * @throws Exception
     */
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        //1.根据url获取HandlerMapping
        HandlerMapping handlerMapping = getHandler(req);
        if (handlerMapping == null){
            processDispatchResult(req,resp,new ModelAndView("404"));
            return;
        }
        //2.根据HandlerMapping拿到HandlerAdapter
        HandlerAdapter handlerAdapter = getHandlerAdapter(handlerMapping);
        //3.根据HandlerAdapter拿到对应的ModelAndView
        ModelAndView modelAndView = handlerAdapter.handle(req, resp, handlerMapping);
        //4.根据ViewResolver找到对应View对象
        processDispatchResult(req,resp,modelAndView);
    }

    private HandlerAdapter getHandlerAdapter(HandlerMapping handlerMapping) {
        if (this.handlerAdapters.isEmpty()){return null;}
        HandlerAdapter handlerAdapter = this.handlerAdapters.get(handlerMapping);
        return handlerAdapter;
    }

    private void processDispatchResult(HttpServletRequest req,HttpServletResponse resp,ModelAndView mv) throws Exception {
        if (mv == null){ return; }
        if (this.viewResolvers.isEmpty()){return;}
        for (ViewResolver viewResolver : this.viewResolvers) {
            View view = viewResolver.resolveViewName(mv.getViewName());
            view.render(mv.getModel(), req,resp);
            return;
        }
    }

    /**
     * 根据url获取HandlerMapping
     * @param req
     * @return
     */
    private HandlerMapping getHandler(HttpServletRequest req){
        String url = req.getRequestURI();
        //项目根路径
        String contextPath = req.getContextPath();
        //去掉项目根路径，将多个 "/" 变为单个 "/"
        url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
        for (HandlerMapping handlerMapping : this.handlerMappings) {
            Matcher matcher = handlerMapping.getPattern().matcher(url);
            if (!matcher.matches()){continue;}
            return handlerMapping;
        }
        return null;
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        applicationContext = new ApplicationContext(config.getInitParameter("contextConfigLocation"));

        //==============MVC部分=====================
        //初始化HandlerMapping
        initStrategies(applicationContext);

        System.out.println("Mini Spring Framework init success!");
    }

    /**
     * 初始化策略（原本是初始化九大组件，重要的是这三个）
     * @param applicationContext
     */
    protected void initStrategies(ApplicationContext applicationContext){
        //扫描controller方法 -> handlerMapping
        initHandlerMapping(applicationContext);
        //初始化参数适配器
        initHandlerAdapters(applicationContext);
        //初始化视图解析器
        initViewResolvers(applicationContext);
    }

    /**
     * 初始化视图解析器
     * @param applicationContext
     */
    private void initViewResolvers(ApplicationContext applicationContext) {
        String templateRoot = applicationContext.getConfig().getProperty("templateRoot");
        //获得视图配置根路径
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
        File templateRootDir = new File(templateRootPath);

        for (File file : templateRootDir.listFiles()) {
            this.viewResolvers.add(new ViewResolver(templateRoot));
        }
    }

    /**
     * 初始化参数适配器
     * @param applicationContext
     */
    private void initHandlerAdapters(ApplicationContext applicationContext) {
        for (HandlerMapping handlerMapping : handlerMappings) {
            handlerAdapters.put(handlerMapping,new HandlerAdapter());
        }
    }

    /**
     * 初始化HandlerMapping(只需要处理Controller)
     */
    private void initHandlerMapping(ApplicationContext applicationContext) {
        if (this.applicationContext.getBeanDefinitionCount()==0){return;}
        for (String beanName : this.applicationContext.getBeanDefinitionNames()) {
            Object instance = applicationContext.getBean(beanName);
            Class<?> clazz = instance.getClass();
            if (!clazz.isAnnotationPresent(Controller.class)){continue;}
            //Controller上的RequestMapping的根路径
            String baseUrl = "";
            if (clazz.isAnnotationPresent(RequestMapping.class)){
                RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
                baseUrl = requestMapping.value();
            }
            //只迭代public方法
            for (Method method : clazz.getMethods()) {
                //没加RequestMapping的方法不需要路由
                if (!method.isAnnotationPresent(RequestMapping.class)){continue;}
                RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);
                String methodUrl = methodRequestMapping.value();
                methodUrl = methodUrl.startsWith("/") ? methodUrl : "/" + methodUrl;

                String regex = ("/" + baseUrl + "/" + methodUrl).replaceAll("\\*", ".*").replaceAll("/+", "/");
                Pattern pattern = Pattern.compile(regex);

                handlerMappings.add(new HandlerMapping(instance,method,pattern));
                System.out.println("mapping:" + regex + "--->" + method);
            }
        }
    }

    /**
     * 首字符变小写
     * @param simpleName
     * @return
     */
    private String toLowerFirstCase(String simpleName){
        char[] charArray = simpleName.toCharArray();
        charArray[0] += 32;
        return String.valueOf(charArray);
    }

}
